__version__ = '0.0.7';
'use strict';


IDs = { 'num': 0, };
class pc {

    constructor(id = null, exchange_method = 'text', roll = 'sender_adaptive', show_state = true, ) {
        if (id == null) {
            id = 'pc_' + IDs['num'];
        }
        if (id in IDs) {
            IDs[id] += 1;
            id += IDs[id];

        }
        this.id = id;
        IDs[this.id] = 1;
        IDs['num'] += 1;

        if ($.type(exchange_method) === "object") {
            this.ex_methods = exchange_method;
        } else if ($.type(exchange_method) === "string") {
            const Ex_methods = {
                'debug':this._debug_exchange_methods,
                'text': this._text_exchange_methods,
                'qrcode': this._scan_qrcode_exchange_methods,
                'server': this._server_exchange
            }
            if (exchange_method in Ex_methods) {
                this.ex_methods = Ex_methods[exchange_method];
                
            } else {
                log(`(${this.id})exchange_method未找到:` + exchange_method);
                this.ex_methods = this._diy_exchange_methods
            }
        }
        this.ex_methods.init(this);
        if (show_state) this._show_state_init();
        this.roll = roll;
        this._make_pc();

    };

    _make_pc(){
        const config = {
            iceServers: [{ urls: "stun:stun.ideasip.com" }]
        }
        log(this.id + ":生成pc，配置为:" + JSON.stringify(config) + "roll:" + this.roll);
        this.pc = new RTCPeerConnection(config);

        if (show_state) this._show_state_set();
        this.ex_methods.set(this);
        this.pc.addEventListener("connectionstatechange",(e) => {
            if (e.target.connectionState === "connected") {
                this.roll = 'connected';
                log("roll修改为connected");
            }
        });

        let self_candidates = [];
        this.pc.onicecandidate = ({ candidate }) => {
            if (candidate) {
                self_candidates.push(candidate)
            } else {
                log(`(${this.id})本轮candidate生成完毕,共` + self_candidates.length + "个!")
                this.ex_methods.send_candis(this,self_candidates);
                self_candidates = [];
            }
        };

        if (this.roll == 'sender_adaptive' || this.roll == 'sender_only') {
            // 存疑，是否应该主动创建而非事件触发
            this.pc.onnegotiationneeded = async () => {
                const offer = await this.pc.createOffer()
                await this.pc.setLocalDescription(offer);
                log(`(${this.id})self_description(offer)已生成`);
                this.ex_methods.send_desc(this,this.pc.localDescription);
            }
        } else {
            log(`(${this.id})等待offer传入`);
        };
    };

    close(){
        this.pc.close();
        this.pc = null;
        // if (this.TimeIntervals)
    };

    _recive_desc = async (input_description) => {

        if(this.pc.connectionState === "connected"){
            return;
        }
        switch (this.roll) {
            case 'sender_adaptive':
                if (input_description.type == "offer") {
                    this.roll = 'reciver_adaptive';
                    this.close();
                    this._make_pc();
                }
                break;
            case 'reciver_adaptive':
                if (input_description.type == "answer") {
                    this.roll = 'sender_adaptive';
                    this.close();
                    this._make_pc();
                }
                break;
            case 'sender_only':
                if (input_description.type == "offer") {
                    log(`(${this.id})只接受answer,但是输入了offer`);
                    return;
                }
                break;
            case 'reciver_only':
                if (input_description.type == "answer") {
                    log(`(${this.id})只接受offer,但是输入了answer`);
                    return;
                }
                break;

            case 'connected':
                return;
        }

        if (input_description.type == "offer") {
            // await this.pc.setLocalDescription({ type: "rollback" })
            await this.pc.setRemoteDescription(input_description);
            const answer = await this.pc.createAnswer()
            await this.pc.setLocalDescription(answer);
            log(`(${this.id})offer description已处理，answer description已生成，请回复`);
            this.ex_methods.send_desc(this,this.pc.localDescription);
            
        } else {
            await this.pc.setRemoteDescription(input_description);
            log(`(${this.id})answer description已处理`);
        }
    };

    _recive_candis = async (input_candidates) => {
        for (const i in input_candidates) {

            if (input_candidates[i]) {
                await this.pc.addIceCandidate(input_candidates[i]);
            };
        };
        log(`${this.id}远程candidates已加入`);
    };

    _diy_exchange_methods = {
        'init': (self) => { },
        'set': (self)=> { },
        'send_desc': (self,localDescription) => {

        },
        'send_candis': (self,self_candidates) => {

        },
    };

    _debug_exchange_methods = {
        'init': (self) => {
            // 加入UI
            append_html(`
            <div id="${this.id}_manully_exchange_div" style="max-width: 500px; background-color:#CC6699">
            <p>${this.id}的candidate:</p>
            <p id="${this.id}_self_candidate_str" style="background-color:#00FFFF">等待candidate</p>
            <p>${this.id}传入candidate输入区:</p>
            <textarea id="${this.id}_input_candidate_textarea"></textarea>
            <button id="${this.id}_confirm_input_candidate_btn" disabled>确认传入</button>
        
            <p>${this.id}的description:</p>
            <p id="${this.id}_self_description_str" style="background-color:#C0C0C0">等待desc</p>
            <p>${this.id}传入description输入区(${{
                    'sender_adaptive': '自适应',
                    'reciver_adaptive': '自适应',
                    'sender_only': '只接受answer',
                    'reciver_only': '只接受offer',
                }[this.roll]}):</p>
            <textarea id="${this.id}_input_description_textarea"></textarea>
            <button id="${this.id}_confirm_input_description_btn" disabled>确认传入</button>
            </div>
            `)

            // 设置UI
            $(`#${this.id}_confirm_input_candidate_btn`).click(c_i_candi.bind(this));

            $(`#${this.id}_confirm_input_description_btn`).click(c_i_desc.bind(this));

            $(`#${this.id}_input_candidate_textarea`).change(() => { $(`#${this.id}_confirm_input_candidate_btn`).attr("disabled", false); });

            $(`#${this.id}_input_description_textarea`).change(() => { $(`#${this.id}_confirm_input_description_btn`).attr("disabled", false); });

            async function c_i_candi() {

                const input_candidates = JSON.parse($(`#${this.id}_input_candidate_textarea`).val());
                await this._recive_candis(input_candidates);
            }

            async function c_i_desc() {

                const input_description = JSON.parse($(`#${this.id}_input_description_textarea`).val());
                await this._recive_desc(input_description);
            }

        },
        'set': (self) => {},
        'send_desc': (self,localDescription) => {
            $(`#${this.id}_self_description_str`).text(JSON.stringify(localDescription));
        },
        'send_candis': (self,self_candidates) => {
            $(`#${this.id}_self_candidate_str`).text(JSON.stringify(self_candidates));
        },
    };

    _text_exchange_methods = {
        'init': (self) => {
            append_html(`<div id="${this.id}_text_exchange_div" style="max-width: 500px; background-color:#CC6699">
            <p>${this.id}的信息:</p>
            <p id="${this.id}_self_text_str" style="background-color:#00FFFF">等待自身信息生成</p>
            <p>${this.id}传入信息输入区:</p>
            <textarea id="${this.id}_input_text_textarea"></textarea>
            <button id="${this.id}_confirm_input_text_btn" disabled>确认传入</button>
            </div>`);
            self.self_info = {
                'candis':'',
                'desc':''
            }

            // 设置UI
            $(`#${this.id}_confirm_input_text_btn`).click(c_i_text.bind(this));

            $(`#${this.id}_input_text_textarea`).change(() => { $(`#${this.id}_confirm_input_text_btn`).attr("disabled", false); });

            async function c_i_text() {

                const input_info = JSON.parse($(`#${this.id}_input_text_textarea`).val());
                await this._recive_desc(input_info.desc);
                await this._recive_candis(input_info.candis);
                
            }
        },
        'set':(self) => {

        },
        'send_desc':(self,localDescription) => {
            self.self_info.desc = localDescription;
            $(`#${this.id}_self_text_str`).text(JSON.stringify(self.self_info));
        },
        'send_candis': (self,self_candidates) => {
            self.self_info.candis = self_candidates;
            $(`#${this.id}_self_text_str`).text(JSON.stringify(self.self_info));
        }
    }

    // 状态显示
    _show_state_init = () => {
        append_html(`
                <div id="${this.id}_show_state_div" style="max-width: 500px; background-color:#9999FF">
                    <div id='${this.id}_iceConnectionState'>${this.id} 的ice candi 状态:</div>
                    <div id='${this.id}_ConnectionState'>${this.id} 的 连接状态:</div>
                    <div id="${this.id}_descriptionState">${this.id} 的 desc状态:</div>
                </div>`)

        // ice_connection_state_change(null);
        this.ice_connection_state_change = (event) => {
            const ice_connection_state = $(`#${this.id}_iceConnectionState`);
            // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState
            switch (event.target.iceConnectionState) {
                case "new":
                    ice_connection_state.append("<p>正在检测网络...(" + event.target.iceConnectionState + ")</p>");
                    break;
                case "checking":
                    ice_connection_state.append("<p>收到远程candi，正在尝试连接...(" + event.target.iceConnectionState + ")</p>");
                    break;
                case "connected":
                    ice_connection_state.append("<p>与远程连接成功，后台依然实时监测(" + event.target.iceConnectionState + ")</p>");
                    break;
                case "completed":
                    ice_connection_state.append("<p>检测活动已经停止(" + event.target.iceConnectionState + ")</p>");
                    break;
                case "failed":
                    ice_connection_state.append("<p>与远程连接失败(" + event.target.iceConnectionState + ")</p>");
                    break;
                case "disconnected":
                    ice_connection_state.append("<p>与远程断开连接(" + event.target.iceConnectionState + ")</p>");
                    break;
                case "closed":
                    ice_connection_state.append("<p>candi服务已经关闭(" + event.target.iceConnectionState + ")</p>");
                    break;
                default:
                    ice_connection_state.append("<p>收到未知状态:(" + event.target.iceConnectionState + ")</p>");
            }
        };

        // connection_state_change(null);
        this.connection_state_change = (event) => {
            const connection_state = $(`#${this.id}_ConnectionState`);
            //   https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionState
            switch (event.target.connectionState) {
                case "new":
                    connection_state.append("<p>收到新的candi信息(" + event.target.connectionState + ")</p>");
                    break;
                case "connecting":
                    connection_state.append("<p>尝试与远程candi连接中...(" + event.target.connectionState + ")</p>");
                    break;
                case "connected":
                    connection_state.append("<p>与所有candi连接成功(" + event.target.connectionState + ")</p>");
                    break;
                case "failed":
                    connection_state.append("<p>与至少一个candi连接失败(" + event.target.connectionState + ")</p>");
                    break;
                case "disconnected":
                    connection_state.append("<p>与至少一个candi断开连接(" + event.target.connectionState + ")</p>");
                    break;
                case "closed":
                    connection_state.append("<p>candi服务已经关闭(" + event.target.connectionState + ")</p>");
                    break;
                default:
                    connection_state.append("<p>收到未知状态:(" + event.target.connectionState + ")</p>");
            }
        }

        this.desc_state_change = (event) => {
            const description_state = $(`#${this.id}_descriptionState`);
            //   https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/signalingState
            switch (event.target.signalingState) {
                case "stable":
                    description_state.append("<p>desc状态稳定(" + event.target.signalingState + ")</p>");
                    break;
                case "have-local-offer":
                    description_state.append("<p>生成本地offer(" + event.target.signalingState + ")</p>");
                    break;
                case "have-remote-offer":
                    description_state.append("<p>收到远程offer(" + event.target.signalingState + ")</p>");
                    break;
                case "have-local-pranswer":
                    description_state.append("<p>生成本地pranswer(" + event.target.signalingState + ")</p>");
                    break;
                case "have-remote-pranswer":
                    description_state.append("<p>收到远程pranswer(" + event.target.signalingState + ")</p>");
                    break;
                default:
                    description_state.append("<p>收到未知状态:(" + event.target.signalingState + ")</p>");
            }
        }
    };
    _show_state_set = () => {
        // this.pc.oniceconnectionstatechange = this.ice_connection_state_change;
        // this.pc.onconnectionstatechange = this.connection_state_change;
        this.pc.addEventListener("iceconnectionstatechange",this.ice_connection_state_change);
        this.pc.addEventListener("connectionstatechange",this.connection_state_change);
        this.pc.addEventListener("signalingstatechange",this.desc_state_change);
    };
};

class data_pc extends pc {
    constructor(onMessage, onopen, onclose, id = null, exchange_method = 'text', roll = 'sender_adaptive', show_state = true) {
        super(id, exchange_method, roll, show_state);
        this.onMessage = onMessage;
        this.onopen = onopen;
        this.onclose = onclose;
        this._make_data_pc();
        this._make_pc = () => {
            super._make_pc();
            this._make_data_pc();
        };
    };

    _make_data_pc(){
        if (this.roll == 'sender_adaptive' || this.roll == 'sender_only') {
            this.Channel = this.pc.createDataChannel("Channel");
            this.Channel.onmessage = this.onMessage;
            this.Channel.onopen = this.onopen;
            this.Channel.onclose = this.onclose;
        }
        else {
            this.pc.ondatachannel = (event) => {
                this.Channel = event.channel;
                this.Channel.onmessage = this.onMessage;
                this.Channel.onopen = this.onopen;
                this.Channel.onclose = this.onclose;
            };
        }

    };

    ;close(){
        ;super.close();
        this.Channel.close();
        this.Channel = null;
    };

};


function append_html(html) {
    $("body").append(html);
};


